﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common.Properties;

#endregion

namespace ScrumTable.Common.Collections
{
    /// <summary>
    /// Specifies an ordered dictionary. The order is defined by the insertion
    /// of the elements.
    /// </summary>
    public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly IDictionary<TKey, DictionaryBucket> _inner = new Dictionary<TKey, DictionaryBucket>();
        private readonly IList<DictionaryBucket> _orderedList = new List<DictionaryBucket>();

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        ///  <see cref="IDictionary{TKey,TValue}.Keys"/>
        /// 
        /// This property is effected by the underlying value insertion order.
        /// </summary>
        public ICollection<TKey> Keys
        {
            get
            {
                return new ReadOnlyCollection<TKey>(
                    from bucket in _orderedList select bucket.Key,
                    _orderedList.Count);
            }
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey,TValue}.Values"/>
        /// 
        /// This property is effected by the underlying value insertion order.
        /// </summary>
        public ICollection<TValue> Values
        {
            get
            {
                return new ReadOnlyCollection<TValue>(
                    from bucket in _orderedList select bucket.Value,
                    _orderedList.Count);
            }
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey,TValue}.get_Item"/>
        /// </summary>
        /// <remarks>
        /// Changing the content of the collection is not supported.
        /// </remarks>
        public TValue this[TKey key]
        {
            get { return _inner[key].Value; }
            set
            {
                PreCondition.AssertNotNull(key, "key");

                if (_inner.ContainsKey(key))
                {
                    _inner[key].Value = value;
                }
                else
                {
                    DictionaryBucket newBucket = new DictionaryBucket
                                                     {
                                                         Index = _orderedList.Count,
                                                         Value = value,
                                                         Key = key
                                                     };
                    _inner[key] = newBucket;
                    _orderedList.Add(newBucket);
                }
            }
        }

        /// <summary>
        ///  <see cref="ICollection{T}.Count"/>
        /// </summary>
        public int Count
        {
            get { return _inner.Count; }
        }

        /// <summary>
        /// Changing the content of the collection is not supported.
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new OrderedDictionary instance without data.
        /// </summary>
        public OrderedDictionary()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        ///  <see cref="IEnumerable{T}.GetEnumerator"/>
        /// 
        /// This enumerator is effected by the underlying value insertion order.
        /// </summary>
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return (from bucket in _orderedList
                    select new KeyValuePair<TKey, TValue>(bucket.Key, bucket.Value)).GetEnumerator();
        }

        /// <summary>
        ///  <see cref="IEnumerable{T}.GetEnumerator"/>
        /// 
        /// This enumerator is effected by the underlying value insertion order.
        /// </summary>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return ((System.Collections.IEnumerable)_inner).GetEnumerator();
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey,TValue}.ContainsKey"/>
        /// </summary>
        public bool ContainsKey(TKey key)
        {
            return _inner.ContainsKey(key);
        }

        /// <summary>
        ///  <see cref="ICollection{T}.Contains"/>
        /// </summary>
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return (_inner.ContainsKey(item.Key) && Equals(_inner[item.Key], item.Value));
        }

        /// <summary>
        ///  <see cref="ICollection{T}.CopyTo"/>
        /// 
        /// This procedure is effected by the underlying value insertion order.
        /// </summary>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _orderedList.Convert(data => new KeyValuePair<TKey, TValue>(data.Key, data.Value)).CopyTo(array, arrayIndex);
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey,TValue}.TryGetValue"/>
        /// </summary>
        public bool TryGetValue(TKey key, out TValue value)
        {
            DictionaryBucket foundBucket;
            
            if (_inner.TryGetValue(key, out foundBucket))
            {
                value = foundBucket.Value;
                return true;
            }

            value = default(TValue);
            return false;
        }

        /// <summary>
        ///  <see cref="ICollection{T}.Add"/>
        /// </summary>
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            PreCondition.AssertNotNull(item, "item");

            ((IDictionary<TKey, TValue>)this).Add(item.Key, item.Value);
        }

        /// <summary>
        ///  <see cref="ICollection{T}.Clear"/>
        /// </summary>
        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            _inner.Clear();
            _orderedList.Clear();
        }

        /// <summary>
        ///  <see cref="ICollection{T}.Remove"/>
        /// </summary>
        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            PreCondition.AssertNotNull(item, "item");

            return ((IDictionary<TKey, TValue>)this).Remove(item.Key);
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey, TValue}.Add(TKey, TValue)"/>
        /// </summary>
        void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
        {
            PreCondition.AssertNotNull(key, "key");
            PreCondition.AssertTrue(!_inner.ContainsKey(key), string.Format(Resources.ExcOrdDictDuplicateEntry, key));

            this[key] = value;
        }

        /// <summary>
        ///  <see cref="IDictionary{TKey, TValue}.Remove(TKey)"/>
        /// </summary>
        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            if (_inner.ContainsKey(key))
            {
                DictionaryBucket existingBucket = _inner[key];
                _orderedList.RemoveAt(existingBucket.Index);
                return _inner.Remove(key);
            }
            return false;
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion


        //--------------------------------------------------------------------
        // Nested Classes
        //--------------------------------------------------------------------

        /// <summary>
        /// Represents a bucket which references to the internal sorted list.
        /// </summary>
        private class DictionaryBucket
        {
            #region Properties
            //--------------------------------------------------------------------
            // Properties
            //--------------------------------------------------------------------

            internal int Index { get; set; }
            internal TValue Value { get; set; }
            internal TKey Key { get; set; }

            #endregion
        }
    }
}